package com.ruyuan.seckill.service.impl;

import com.alibaba.fastjson.JSON;
import com.ruyuan.seckill.cache.Cache;
import com.ruyuan.seckill.domain.enums.QuantityType;
import com.ruyuan.seckill.domain.vo.GoodsQuantityVO;
import com.ruyuan.seckill.service.GoodsQuantityManager;
import com.ruyuan.seckill.utils.StockCacheKeyUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.ClassPathResource;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.script.RedisScript;
import org.springframework.scripting.ScriptSource;
import org.springframework.scripting.support.ResourceScriptSource;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

/**
 * 商品库存接口
 *
 * 采用lua脚本执行redis中的库存扣减
 * 数据库的更新采用非时时同步
 * 而是建立了一个缓冲池，当达到一定条件时再同步数据库
 * 这样条件有：缓冲区大小，缓冲次数，缓冲时间
 * 上述条件在配置中心可以配置，如果没有配置采用 默认值
 */
@Service
@Slf4j
public class GoodsQuantityManagerImpl implements GoodsQuantityManager {


    @Autowired
    public StringRedisTemplate stringRedisTemplate;
    @Autowired
    private Cache cache;
    private static RedisScript<Boolean> script = null;

    private static RedisScript<Boolean> getRedisScript() {

        if (script != null) {
            return script;
        }

        ScriptSource scriptSource = new ResourceScriptSource(new ClassPathResource("sku_quantity.lua"));
        String str = null;
        try {
            str = scriptSource.getScriptAsString();
        } catch (IOException e) {
            e.printStackTrace();
        }

        script = RedisScript.of(str, Boolean.class);
        return script;
    }



    /**
     * 秒杀库存更新接口
     *
     * @param goodsQuantityList 要更新的库存vo List
     * @return 如果更新成功返回真，否则返回假
     */
    @Override
    public Boolean updateSeckillSkuQuantity(List<GoodsQuantityVO> goodsQuantityList) {

        List<Integer> skuIdList = new ArrayList<>();
        List<Integer> goodsIdList = new ArrayList<>();

        List<String> keys = new ArrayList<>();
        List<String> values = new ArrayList<>();
        for (GoodsQuantityVO quantity : goodsQuantityList) {
            Assert.notNull(quantity.getGoodsId(), "goods id must not be null");
            Assert.notNull(quantity.getSkuId(), "sku id must not be null");
            Assert.notNull(quantity.getQuantity(), "quantity id must not be null");
            Assert.notNull(quantity.getQuantityType(), "Type must not be null");

            // 构造SKU可销售库存的KEY 并加入 keys的list结构中
            if (QuantityType.enable.equals(quantity.getQuantityType())) {
                keys.add(StockCacheKeyUtil.skuEnableKey(quantity.getSkuId()));
            } else if (QuantityType.actual.equals(quantity.getQuantityType())) {
                keys.add(StockCacheKeyUtil.skuActualKey(quantity.getSkuId()));
            }
            // 将要扣减的SKU可销售库存的数量加入到values的list中 注意 key与value是两个list结构 他们的值是按序一一对应的
            values.add(String.valueOf(quantity.getQuantity()));
            // 构造商品可销售库存的KEY 并加入 keys的list结构中
            if (QuantityType.enable.equals(quantity.getQuantityType())) {
                keys.add(StockCacheKeyUtil.goodsEnableKey(quantity.getGoodsId()));
            } else if (QuantityType.actual.equals(quantity.getQuantityType())) {
                keys.add(StockCacheKeyUtil.goodsActualKey(quantity.getGoodsId()));
            }
            // 将要扣减的商品可销售库存的数量加入到values的list中 注意 key与value是两个list结构 他们的值是按序一一对应的
            values.add(String.valueOf(quantity.getQuantity()));

            skuIdList.add(quantity.getSkuId());
            goodsIdList.add(quantity.getGoodsId());
        }

        List list = cache.multiGet(keys);
        log.info("goods stock:"+ JSON.toJSONString(list));
        // 获取扣减库存的LUA脚本
        RedisScript<Boolean> redisScript = getRedisScript();
        // 将keys 与 values 传入到LUA脚本中 keys表示要扣减库存的缓存key是什么 values表示 对应的keys要扣减多少库存
        Boolean result = stringRedisTemplate.execute(redisScript, keys, values.toArray());
        list = cache.multiGet(keys);
        log.info("goods stock:"+ JSON.toJSONString(list));
        log.info("更新库存：");
        log.info(goodsQuantityList.toString());
        log.info("更新结果：" + result);

        return result;
    }

}
